// ==UserScript==
// @name           DynaScriptRuntime
// @namespace      jirina42@seznam.cz
// @include        http*://orion.pardus.at/*
// @version        2.6
// @run-at document-start
// @unwrap
// ==/UserScript==

// updateURL https://www.example.com/myscript.meta.js
// downloadURL https://www.example.com/myscript.user.js

function readCookie(name) {
    return localStorage.getItem(name);
}

function createCookie(name,value) {
    localStorage.setItem(name, value);
}

function checkScriptAlreadyInstalled (head, src) {
    var scripts = head.getElementsByTagName("script");
    for (var i=0; i<scripts.length; i++) {
        var aSrc = scripts[i].getAttribute("id");
        if (!aSrc) continue;
        if (aSrc.indexOf(src) != -1) return true;
    }
}

function getLocalDev() {
    if (localStorage.getItem('ds_local_dev') == "true") {
		return true
    } else {
		return false
	}
}

function getServerUrl(file){

    if (getLocalDev() == true) {
		url = localStorage.getItem('ds_local_dev_path');
		if (url == null) var url = localStorage.getItem('ds_dynascript_address');
    } else {
		var url = localStorage.getItem('ds_dynascript_address');
    }
	
    if (url == null) {
        if (!localStorage.getItem ("po_invalid_url_shown")) {
            alert ("Dynascript server address is not set or it's invalid. Please ender valid server URL in the DynaScript control panel. You'll see this warning only once but none of the scripts requiring the server address will work until you enter a valid one.");
            localStorage.setItem ("po_invalid_url_shown", "yes");
        }
        return null;
    }
	
    if (file) {
        if (file[0] != "\/") url += "\/";
        url += file;
    }
	
    return url;
	
}

function getScriptObject(aScript, head, doc) {
	
	// try to find script - using src as unique identifier
	var scriptId = aScript.getAttribute("src");
	
	// skip further processing if script already injected to current loading page
	if (checkScriptAlreadyInstalled(head, scriptId)){
//GM_log ("Script already installed: " + aScript.parentNode.getAttribute("scriptName"));
		return null;
	}
	
	// Create a new <script> element
	var myScript = doc.createElement("script");
	myScript.language = "javascript";
	myScript.type = "text/javascript";
	myScript.id = scriptId;
	
	if (getLocalDev()) {
		// get source from file, never cache in case of local dev
		myScript.src = getServerUrl(scriptId);
	} else {
		// try to get source from storage
		var scriptSource = localStorage.getItem(scriptId);
		// if source was not found then report error in log
		if (!scriptSource || scriptSource == "") {
			GM_log("Script source was not found, please reinstall " + aScript.parentNode.getAttribute("scriptName") + " from Dynascript control panel");
			scriptSource = "";
		}
		// set script source
		myScript.text = scriptSource;
	}
	
	return myScript;
	
}

function checkForNewScripts() {

    // Check the server for the new scripts

	GM_log("Performing daily check for updates...");
	
	function setMsg(msg) {
		// set permanent annoying message in message bar (clears when CP loads)
		localStorage.DynaScriptMSG = msg;
	}
	
	function findScriptIn(name, scripts) {
		for (var i=0; i<scripts.childNodes.length; i++) {
			if (scripts.childNodes[i].getAttribute ("scriptName") == name)
				return scripts.childNodes[i];
		}
		return null;
	}
	
	function checkInstallVersions(scripts) {

		// check available versions against installed versions
		var scriptsInstalled = document.createElement("div");
		var str = readCookie("DS_installedScriptsConfig_" + getUniverse());
		
		// when no scripts installed then nothing to check
		if (str) 
			scriptsInstalled.innerHTML = str;
		else
			return;

		// check versions for each installed script
		for (var i=0; i<scripts.childNodes.length; i++) {
			var aScript = scripts.childNodes[i];
			iScript = findScriptIn(aScript.getAttribute("scriptName"), scriptsInstalled);
			if (!iScript) continue;
//GM_log("checking: " + aScript.getAttribute("scriptName") + "inst: " + iScript.getAttribute("version") + " / avail: " + aScript.getAttribute("version"));
			if (iScript.getAttribute("version") != aScript.getAttribute("version")) {
				GM_log(aScript.getAttribute("scriptName") + " installed v" + iScript.getAttribute("version") + " / available: " + aScript.getAttribute("version"));
				setMsg("Dynascript updates available in control panel!");
				return;
			}
		}
		
		GM_log("no version changes");
		
	}

	function getUniverse () {
		return document.URL.split ("//")[1].split(".")[0];
	}
	
	function getPilotID () {
		var uid = unsafeWindow.userid;
		if (uid != undefined) {
			localStorage.setItem("po_pilot_id", uid);
			return uid;
		}
		return localStorage.getItem("po_pilot_id");
	}

	function getUser () {
		try {
			return top.window.frames[1].document.getElementById('universe').getAttribute('title').substring(7);
		} catch (ex) {
		return localStorage.getItem ("po_pilot_name");
		}
		return null;
	}

	function getValidationCode(){
		var code = localStorage.getItem('po_sc');
		if ((code == null) || (code.length < 20)) {
			if (!localStorage.getItem ("po_invalid_code_shown")) {
				alert ("Security code invalid. Please ender valid security code in the DynaScript control panel. You'll see this warning only once but none of the scripts requiring the security code will work until you enter a valid code.");
				localStorage.setItem ("po_invalid_code_shown", "yes");
			}
			return "...";
		}
		return code;
	}

    var uni = getUniverse();
    var addr = getServerUrl("/dynascript/dynascript.php");
    if (!addr) return;
    addr += "?universe=" + uni + "&user=" + getUser() + "&userid=" + getPilotID() + "&vcode=" + getValidationCode();
//GM_log("Addr:" + addr);
	
    var sFailMsg = "Failed to check for scripts updates";
	
    GM_xmlhttpRequest({
        method: "GET",
        url: addr,
        onload: function(response) {
		
            var responseOK = false;
			var responseEmpty = false;
            var div = document.createElement("div");
			
            if (!response.responseText || response.responseText.length == 0) {
                responseEmpty = true;
            } else {
                div.innerHTML = response.responseText;
                try {
					// TODO: check for kick and ban = remove scripts
                    if (div.firstChild.tagName == "DIV" && div.firstChild.firstChild.tagName == "SCRIPT") responseOK = true;
                } catch (ex) {
					setMsg(sFailMsg);
					GM_log(sFailMsg + ". Exception: " + ex.message);
				}
            }
			
			if (responseEmpty) {
				setMsg(sFailMsg);
				GM_log(sFailMsg + ". Empty response received");
			}
			
			if (responseOK) {
				// save when data received ok
				createCookie("DS_availableScriptsConfig_" + uni, response.responseText);
				checkInstallVersions(div);
			}
			
			close(response);
			
        },
        onerror: function (response){
			setMsg(sFailMsg);
			GM_log(sFailMsg + ". Error: " + response.responseText);
			close();
		},
        onabort: function (response){
			setMsg(sFailMsg);
			GM_log(sFailMsg + ". Abort: " + response.responseText);
			close();
		}
    });
	
}

function checkForUpdates(doc) {

//localStorage.removeItem("DS_lastCheck");
	
	if (!localStorage.DynaScriptMSG) {

		// clear msg if still showing
		var dsMsg = doc.getElementById('dsMsg');
		if (dsMsg) doc.removeChild(dsMsg);
		
		// Get the date of the last version check. If it's more than one day old, check for new versions. Otherwise exit.
		var lastCheck = readCookie("DS_lastCheck");
		if (!lastCheck)
			lastCheck = 0;
		else
			lastCheck = Number(lastCheck);

		var today = new Date();
		var todayCheck = Number(String(today.getFullYear()) + String(today.getMonth()) + String(today.getDate()));
		
		if (todayCheck <= lastCheck) return;
		// OK, the last check took place yesterday or further in the past (or never). Let's check for new versions.
		checkForNewScripts();
		createCookie("DS_lastCheck", String(todayCheck));
		
	}

	// show updates available link until user goes to CP (cp clears it until next check)
	if (localStorage.DynaScriptMSG) {
		var dsMsg = doc.getElementById('dsMsg');
		if (!dsMsg) {
			var dsMsg = doc.createElement('div');
			dsMsg.id = 'dsMsg';
			var table = doc.getElementsByTagName('table');
			var e = table[0].firstChild.firstChild.childNodes[3];
			dsMsg.innerHTML = '<a href="options.php" target="main" style="color:red">' + localStorage.DynaScriptMSG + '</a>';
			e.appendChild(dsMsg);
		}
	}


}

function loadScriptsForPage(doc) {

	// some script-created hidden frames can get called on DOMFrameContentLoaded, prevent further processing
	if (doc.URL == 'about:blank') return;

    // Load the scripts for the given frame
    // Get the current URL code
    // So if the URL is e.g. http://orion.pardus.at/statistics.php?display=onlinelist
    // The result is simply statistics.php
	//try {
		var page = doc.URL.split ("//")[1].split("/")[1].split("?")[0];
		var uni = doc.URL.split ("//")[1].split(".")[0];
	//}
	//catch(err) {
	//	GM_log("error in doc.URL.split:" + err.message);
	//	GM_log(doc.URL);
	//	return;
	//}
	
	// daily version check
	if(doc.location.pathname.search(/msgframe.php/i) != -1) checkForUpdates(doc);

    // Loads the scripts applicable for the given page
    var scripts = readCookie ("DS_installedScriptsConfig_"+uni);
    if (!scripts) return;
    
    // Decode the saved script elements and add them into the document's head to load them actually
    var div = doc.createElement ("div");
    var head = doc.getElementsByTagName('head')[0];
	
    div.innerHTML = scripts;
    var scripts = div.getElementsByTagName ("script");

    // Loop over the scripts and install them
    for (var i=0; i<scripts.length; i++) {
        aScript = scripts[i];

		var includes = aScript.getAttribute ("includes");
	
		// Skip scripts which do not have the "includes" attribute or the script should not be included into the current page
		if (!includes || includes.indexOf(page) == -1) continue;
	      
		// Check if the script's target ir the page DOM and install the script if so
        if (aScript.parentNode.getAttribute ("target") == "page") {
			// Load script source from menu frame or server if not found in menu frame
			var myScript = getScriptObject(aScript, head, doc);
			if (myScript) {
//GM_log("Injecting script: " + aScript.parentNode.getAttribute("scriptName") + " into " + page);
				head.appendChild(myScript);
			}
		}
    }
	
}

function triggerLoad(e) {
	if (e.type == 'DOMFrameContentLoaded') {
//GM_log("DOMFrameContentLoaded for frame " + e.target.name);
		for (var i=0; i<frames.length; i++) {
			if (frames[i].name == e.target.name) {
//GM_log("DOMFrameContentLoaded calling loader / " + e.target.name + " / " + frames[i].document.location.pathname);
				loadScriptsForPage(frames[i].document);
			}
		}
	} else {
//GM_log("DOMContentLoaded for " + document.URL);
		loadScriptsForPage(document);
	}
}

// hook frame loading event only for main frame
if (location.pathname.indexOf('game.php') != -1) {
	document.addEventListener('DOMContentLoaded', triggerLoad, true);
	document.addEventListener('DOMFrameContentLoaded', triggerLoad, true);
	// store this script version for CP
	sessionStorage.DynaScriptRTV = GM_info.script.version;	
} else {
	// skip hooking pages inside main frame as those trigger on frame content load
	if (parent.location.pathname.indexOf('game.php') == -1) {
//GM_log("hooking DOMContentLoaded for " + document.URL);
		document.addEventListener('DOMContentLoaded', triggerLoad, true);
	}
}